"use strict";


// Constants for recieved controller states
var kLeftPadLeftState = 0;
var kLeftPadRightState = 1;
var kLeftPadUpState = 2;
var kLeftPadDownState = 3;
var kRightPadLeftState = 4;
var kRightPadRightState = 5;
var kRightPadUpState = 6;
var kRightPadDownState = 7;
var kAButtonState = 8;
var kBButtonState = 9;
var kXButtonState = 10;
var kLeftShoulderState = 11;
var kRightShoulderState = 12;
var kStartButtonState = 13;
var kBackButtonState = 14;
var kDpadUpState = 15;
var kDpadDownState = 16;
var kDpadLeftState = 17;
var kDpadRightState = 18;

var kScrollInterval = 100;
var theTabs = [];
var theNeighbours = [];
var thePosition = 0;
var alertIndex;
var buttons;
var debugNeighbours = 0;
var coloursCached = 0;

var prevColours = {
		this:"",
		up:"",
		down:"",
		left:"",
		right:""};

startupPostPropertyLoadTasks.push(InitialiseGamepadController);

function loadTabs()
{
	// The tabs don't load in position order, so sort them before pushing
	$(".tabSelect[data-tab-position]").sort(function(a,b) {
     return a.getAttribute('data-tab-position') > b.getAttribute('data-tab-position') ? 1 : -1;
	}).each(function() {
		if(!$(this).hasClass("noDisplay"))
		{
			theTabs.push($(this));
		}
	});
}

function ajaxTab(prefix, suffix, tabName)
{
	// Return a bool of whether the import was successful
	var result = false;
	$.ajax(
	{
		url: prefix + "feralUIImports/" + tabName + "/" + tabName + suffix + ".json",
		type:'HEAD',
		success: function()
		{
			$.getJSON(prefix + "feralUIImports/" + tabName + "/" + tabName + suffix + ".json", function (data)
			{
				theNeighbours.push(data);
			});
			result = true;
		}
	});
	return result;
}

function loadJSON(tabNumber)
{
	// Initialise for the DRM delivery method (i.e. no suffix)
	var suffix = "";
	if($("html").hasClass("deliverySTM"))
	{
		suffix = "STM";
	}
	else if($("html").hasClass("deliveryMAS"))
	{
		suffix = "MAS";
	}

	// A file for each tab
	if(tabNumber < theTabs.length)
	{
		// As long as the data-tab-id attribute is properly named with tabContentXXXXX
		// It should agree with the folder, tabXXXXX and the file tabXXXXX.json
		var tabName = "tab" + (theTabs[tabNumber].attr("data-tab-id")).substring(10);

		// Check for custom PGOW json WITH delivery suffix
		if (!ajaxTab("../../GamePGOW/", suffix, tabName))
		{
			// Check for custom PGOW json WITHOUT delivery suffix
			if (!ajaxTab("../../GamePGOW/", "", tabName))
			{
				// Check for standard PGOW json WITH delivery suffix
				if (!ajaxTab("", suffix, tabName))
				{
					// Check for standard PGOW json WITHOUT delivery suffix
					if (!ajaxTab("", "", tabName))
					{
						console.log("No json file found for " + tabName);
						theNeighbours.push([]);
					}
				}
			}
		}
		// Load next tab
		loadJSON(tabNumber + 1);
	}
}

function InitialiseGamepadController()
{
	var enabled;

	// Get gamepad state from the C++ side GamepadInputHandler.cpp
	var thePromise = hookPropertyToFunction("EnableGamepadInPGOW",
																					"bool",
		function(propertyName, typePattern, propValueArray)
		{

			enabled = propValueArray[0];

		});

	thePromise.then(function()
	{
		if(enabled)
		{
			// Use object so we can pass it around by reference
			var focusElement = {element: undefined};

			// Load the tabs
			loadTabs();

			// Bit hacky, but turn off async to make sure everything loads in the proper order
			// Nicer than having a tonne of .then()s
			jQuery.ajaxSetup({async:false});

			// Load json files in order
			loadJSON(0);

			// Turn the async back on
			jQuery.ajaxSetup({async:true});
		}

		// Get gamepad state from the C++ side GamepadInputHandler.cpp
		var gamepadPromise = hookPropertyToFunction("InputChange",
																								"array<bool>",
			function(propertyName, typePattern, propValueArray)
			{

				// Act on the gamepad state
				if(enabled)
				{
					handleInput(propValueArray, focusElement);
				}

				return true;

			});

		return gamepadPromise;

	});

	return thePromise;
}

function handleInput(gamepadStateArray, focusElement)
{
	// Grab the currently selected tab name, using the selectedTabButton class
	var selectedTabId = $("#tabbar>.selectedTabButton").attr("data-tab-id");

	// Search for the tabs index in the theTabs
	for (var i = 0; i < theTabs.length; i++) {
		if(theTabs[i].attr("data-tab-id") == selectedTabId)
		{
			thePosition = i;
		}
	}

	if(testerMode && gamepadStateArray[0][kXButtonState])
	{
		if(!debugNeighbours)
		{
			debugNeighbours = 1;
			if(!$(document.activeElement).is("body"))
			{
				showNeighbours(thePosition, false);
				coloursCached = 1;
			}
		}
		else
		{
			debugNeighbours = 0;
			showNeighbours(thePosition, true);
		}
	}

	// Check start button
	if(gamepadStateArray[0][kStartButtonState])
	{
		// Reset the focusElement.element in case the launch is cancelled
		focusElement.element = undefined;
		launchGame();
	}

	// Check back button
	if(gamepadStateArray[0][kBackButtonState])
	{
		// Reset the focusElement.element in case quit is cancelled
		focusElement.element = undefined;
		$("#main-quitButton").click();
	}

	// Check shoulder buttons
	if(gamepadStateArray[0][kLeftShoulderState] || gamepadStateArray[0][kRightShoulderState])
	{
		var newPosition = (thePosition + (gamepadStateArray[0][kLeftShoulderState] ? (-1) : 1));
		// Ignore activation tab, making the assumption it is element 0
		if(theNeighbours[newPosition])
		{
			if(debugNeighbours)
			{
				showNeighbours(thePosition, true);
			}
			// Fixing the strange tab switch bug, caused by focussed elements
			if(focusElement.element !== undefined)
			{
				focusElement.element.blur();
				focusElement.element = undefined;
			}
			// Also needed to deal with tab switch animation not working as intended
			// Give it a bit of time to blur
			setTimeout(switchTab, 20, theTabs[newPosition]);
			// Some elements have the autofocus property, and try and focus before
			// the animation is sorted. Causes some weird bugs.
			if(!$(document.activeElement).is("body"))
			{
				$(document.activeElement).blur();
			}
			focusElement.element = undefined;
		}
	}

	// Check A button
	if(gamepadStateArray[0][kAButtonState])
	{
		// If the selectric is open
		if(focusElement.element !== undefined && focusElement.element.hasClass("selectricInput")
				&& focusElement.element.parent(".selectricWrapper").hasClass("selectricOpen"))
		{
			var value = focusElement.element.parent().find(".selected").text();
			var e = $.Event("keydown", { keyCode: 13}); // 13 is enter key
			focusElement.element.trigger(e);
			focusElement.element.parent().find("select").val(value).selectric('refresh');
			focusElement.element.focus();
		}
		// Selectric specific
		else if($(document.activeElement).hasClass("selectricInput"))
		{
			focusElement.element.parent().find("select").selectric('open');
		}
		// Pop up windows
		else if($("[data-prevent-close-name='DocumentationDialogPreventClose-ok']").is(":focus") ||
					$("[data-prevent-close-name='DocumentationDialogPreventClose-openInBrowser']").is(":focus"))
		{
			$(document.activeElement).click();
		}
		else
		{
			$(document.activeElement).click();
			alertIndex = undefined;
			buttons = undefined;
		}
	}

	// Check B button
	if(gamepadStateArray[0][kBButtonState])
	{
		if(focusElement.element !== undefined && focusElement.element.hasClass("selectricInput")
				&& focusElement.element.parent(".selectricWrapper").hasClass("selectricOpen"))
		{
			focusElement.element.parent().find("select").selectric('close');
			focusElement.element.focus();
		}
		// Pop up windows
		else if($("[data-prevent-close-name='DocumentationDialogPreventClose-ok']").is(":focus") ||
					$("[data-prevent-close-name='DocumentationDialogPreventClose-openInBrowser']").is(":focus"))
		{
			$("[data-prevent-close-name='DocumentationDialogPreventClose-ok']").click();
		}
		else if($("[data-inner-string='localise-Startup.Close']").parent().is(":focus"))
		{
			$("[data-inner-string='localise-Startup.Close']").parent().click();
		}
	}

	//Navigation

	//Left pad navigation
	if(gamepadStateArray[0][kLeftPadUpState] || gamepadStateArray[0][kLeftPadDownState] ||
		 gamepadStateArray[0][kLeftPadLeftState] || gamepadStateArray[0][kLeftPadRightState])
	{
		if(focusElement.element !== undefined && focusElement.element.hasClass("selectricInput")
			&& focusElement.element.parent(".selectricWrapper").hasClass("selectricOpen"))
		{
			//focusElement.element.blur();
			handleSelectricNavigation(focusElement,
											 					gamepadStateArray[0][kLeftPadUpState],
											 					gamepadStateArray[0][kLeftPadDownState],
											 					gamepadStateArray[0][kLeftPadLeftState],
											 					gamepadStateArray[0][kLeftPadRightState]);
		}
		// Pop ups and web pages
		else if($("[data-prevent-close-name='DocumentationDialogPreventClose-ok']").is(":focus") ||
				$("[data-prevent-close-name='DocumentationDialogPreventClose-openInBrowser']").is(":focus"))
		{
			if(gamepadStateArray[0][kLeftPadLeftState] || gamepadStateArray[0][kLeftPadRightState])
			{
				if($("[data-prevent-close-name='DocumentationDialogPreventClose-ok']").is(":focus"))
				{
					$("[data-prevent-close-name='DocumentationDialogPreventClose-openInBrowser']").focus();
				}
				else
				{
					$("[data-prevent-close-name='DocumentationDialogPreventClose-ok']").focus();
				}
			}
			if(gamepadStateArray[0][kLeftPadUpState])
			{
				$("iframe").contents().scrollTop($("iframe").contents().scrollTop() - kScrollInterval);
			}
			if(gamepadStateArray[0][kLeftPadDownState])
			{
				$("iframe").contents().scrollTop($("iframe").contents().scrollTop() + kScrollInterval);
			}
		}
		// Alerts
		else if ($("#AlertDialog").length && $("#AlertDialog").dialog("isOpen"))
		{
			if(buttons === undefined)
			{
				buttons = $("#AlertDialog").parent().find("input, button").not(".noDisplay, :hidden");
				alertIndex = undefined;
				if(!$(document.activeElement).is("body"))
				{
					focusElement.element = $(document.activeElement);
				}
			}
			handleAlertNavigation(focusElement,
										 				gamepadStateArray[0][kLeftPadUpState],
										 				gamepadStateArray[0][kLeftPadDownState],
										 				gamepadStateArray[0][kLeftPadLeftState],
										 				gamepadStateArray[0][kLeftPadRightState]);
		}
		else if ($("#AlertDialog").length && !$("#AlertDialog").dialog( "isOpen" ) &&
						alertIndex !== undefined)
		{
			alertIndex = undefined;
			buttons = undefined;
			focusElement.element = undefined;
			handleInput(gamepadStateArray, focusElement);
		}
		else
		{
			handleNavigation(focusElement, thePosition,
											 gamepadStateArray[0][kLeftPadUpState],
											 gamepadStateArray[0][kLeftPadDownState],
											 gamepadStateArray[0][kLeftPadLeftState],
											 gamepadStateArray[0][kLeftPadRightState]);
		}
	}

	// Right thumb pad
	if(gamepadStateArray[0][kRightPadUpState] || gamepadStateArray[0][kRightPadDownState])
	{
		if($("[data-prevent-close-name='DocumentationDialogPreventClose-ok']").is(":focus") ||
				$("[data-prevent-close-name='DocumentationDialogPreventClose-openInBrowser']").is(":focus"))
		{
			if(gamepadStateArray[0][kRightPadUpState])
			{
				$("iframe").contents().scrollTop($("iframe").contents().scrollTop() - kScrollInterval);
			}
			if(gamepadStateArray[0][kRightPadDownState])
			{
				$("iframe").contents().scrollTop($("iframe").contents().scrollTop() + kScrollInterval);
			}
		}
	}

	// Dpad navigation
	if(gamepadStateArray[0][kDpadUpState] || gamepadStateArray[0][kDpadDownState] ||
		 gamepadStateArray[0][kDpadLeftState] || gamepadStateArray[0][kDpadRightState])
	{
		if(focusElement.element !== undefined && focusElement.element.hasClass("selectricInput")
				&& focusElement.element.parent(".selectricWrapper").hasClass("selectricOpen"))
		{
			//focusElement.element.blur();
			handleSelectricNavigation(focusElement,
											 				 	gamepadStateArray[0][kDpadUpState],
												 			 	gamepadStateArray[0][kDpadDownState],
											 				 	gamepadStateArray[0][kDpadLeftState],
											 			 	 	gamepadStateArray[0][kDpadRightState]);

		}
		// Pop ups and web pages
		else if($("[data-prevent-close-name='DocumentationDialogPreventClose-ok']").is(":focus") ||
				$("[data-prevent-close-name='DocumentationDialogPreventClose-openInBrowser']").is(":focus"))
		{
			if(gamepadStateArray[0][kDpadLeftState] || gamepadStateArray[0][kDpadRightState])
			{
				if($("[data-prevent-close-name='DocumentationDialogPreventClose-ok']").is(":focus"))
				{
					$("[data-prevent-close-name='DocumentationDialogPreventClose-openInBrowser']").focus();
				}
				else
				{
					$("[data-prevent-close-name='DocumentationDialogPreventClose-ok']").focus();
				}
			}
			if(gamepadStateArray[0][kDpadUpState])
			{
				$("iframe").contents().scrollTop($("iframe").contents().scrollTop() - kScrollInterval);
			}
			if(gamepadStateArray[0][kDpadDownState])
			{
				$("iframe").contents().scrollTop($("iframe").contents().scrollTop() + kScrollInterval);
			}
		}
		// Alerts
		else if ($("#AlertDialog").length && $("#AlertDialog").dialog("isOpen"))
		{
			if(buttons === undefined)
			{
				buttons = $("#AlertDialog").parent().find("input, button").not(".noDisplay, :hidden");
				alertIndex = undefined;
				focusElement.element = $(document.activeElement);
			}
			handleAlertNavigation(focusElement,
														gamepadStateArray[0][kDpadUpState],
			 											gamepadStateArray[0][kDpadDownState],
			 											gamepadStateArray[0][kDpadLeftState],
			 											gamepadStateArray[0][kDpadRightState]);
		}
		else if ($("#AlertDialog").length && !$("#AlertDialog").dialog( "isOpen" ) &&
						alertIndex !== undefined)
		{
			alertIndex = undefined;
			buttons = undefined;
			focusElement.element = undefined;
			handleInput(gamepadStateArray, focusElement);
		}
		else
		{
			handleNavigation(focusElement, thePosition,
											 gamepadStateArray[0][kDpadUpState],
											 gamepadStateArray[0][kDpadDownState],
							  			 gamepadStateArray[0][kDpadLeftState],
											 gamepadStateArray[0][kDpadRightState]);
		}
	}

	// Get rid of focus on click
	$(document).mousedown(function() {
		if(focusElement.element !== undefined)
		{
			if(!$(document).is(':animated') ) {
				focusElement.element.blur();
				focusElement.element = undefined;
			}
		}
	});
}

function handleNavigation(focusElement, thePosition, upPressed, downPressed, leftPressed, rightPressed)
{
	if(debugNeighbours)
	{
		debugNeighbours = 0;
		showNeighbours(thePosition, true);
		coloursCached = 0;
	}

	// An object of the json of the current tab
	var currentTab = theNeighbours[thePosition];
	var newFocus = focusElement.element;
	// An object of the neighbours and selector of the current element
	var theElement = currentTab.elements[currentTab.currentElement];
	var elementIndex = currentTab.currentElement;

	// If the focusElement object is undefined
	if(focusElement.element === undefined)
	{
		newFocus = $(theElement.thisElement);
	}
	else
	{
		// Navigate the elements
		if (upPressed && theElement.upNeighbour != -1)
		{
			currentTab.currentElement = theElement.upNeighbour;
			theElement = currentTab.elements[currentTab.currentElement];
			newFocus = $(theElement.thisElement);
		}
		if (downPressed && theElement.downNeighbour != -1)
		{
			currentTab.currentElement = theElement.downNeighbour;
			theElement = currentTab.elements[currentTab.currentElement];
			newFocus = $(theElement.thisElement);
		}
		if (leftPressed && theElement.leftNeighbour != -1)
		{
			currentTab.currentElement = theElement.leftNeighbour;
			theElement = currentTab.elements[currentTab.currentElement];
			newFocus = $(theElement.thisElement);
		}
		if (rightPressed && theElement.rightNeighbour != -1)
		{
			currentTab.currentElement = theElement.rightNeighbour;
			theElement = currentTab.elements[currentTab.currentElement];
			newFocus = $(theElement.thisElement);
		}
	}
	// Update focus
	if(focusElement.element != newFocus)
	{
		if(newFocus.prop("disabled"))
		{
			console.log("Disabled element");
			// Set everything back to its original value
			newFocus = focusElement.element;
			currentTab.currentElement = elementIndex;
		}

		focusElement.element = newFocus;
		focusElement.element.focus();

	}
}

function handleSelectricNavigation(focusElement, upPressed, downPressed, leftPressed, rightPressed)
{
	// Selectric already has support for arrow buttons, so use that
	if(upPressed)
	{
		var e = $.Event("keydown", { keyCode: 38}); // 38 is keyboard up
		focusElement.element.trigger(e);
	}

	if(downPressed)
	{
		var e = $.Event("keydown", { keyCode: 40}); // 40 is keyboard down
		focusElement.element.trigger(e);
	}
}

function handleAlertNavigation(focusElement, upPressed, downPressed, leftPressed, rightPressed)
{
	// Using an index system for alert buttons, seeing as JQuery objects have been filtered down
	// to those appropriate (i.e. no hidden objects)
	if(alertIndex == undefined)
	{
		// Go for the last button
		alertIndex = buttons.length - 1;
	}
	if(leftPressed)
	{
		if(buttons[alertIndex - 1] !== undefined)
		{
			alertIndex = alertIndex - 1;
		}
	}
	if(rightPressed)
	{
		if(buttons[alertIndex + 1] !== undefined)
		{
			alertIndex = alertIndex + 1;
		}
	}
	if(upPressed)
	{
		if($(buttons[0]).is("#alertDialogCheckbox"))
		{
			alertIndex = 0;
		}
	}
	if(downPressed)
	{
		if(alertIndex == 0 && $(buttons[0]).is("#alertDialogCheckbox"))
		{
			alertIndex = 1;
		}
	}

	if(focusElement.element === undefined || focusElement.element != buttons[alertIndex])
	{
		focusElement.element = $(buttons[alertIndex]);
		focusElement.element.focus();
	}
}

function showNeighbours(thePosition, hide)
{
	// Function to show the neighbours of an element defined in the .json
	var currentTab = theNeighbours[thePosition];
	var thisColour = "yellow";
	var upColour = "darkgreen";
	var downColour = "violet";
	var leftColour = "blue";
	var rightColour = "red";

	if(hide)
	{
		// If we're 'hiding' elements and setting them back to usual
		// just change the background to white
		thisColour = prevColours["this"];
		upColour = prevColours["up"];
		downColour = prevColours["down"];
		leftColour = prevColours["left"];
		rightColour = prevColours["right"];
	}

	if(currentTab.currentElement !== undefined)
	{
		// An object of the neighbours and selector of the current element
		var theElement = currentTab.elements[currentTab.currentElement];

		changeColour($(theElement.thisElement),thisColour,"this",hide);

		if(theElement.upNeighbour != -1)
		{
			var theUpNeighbour = $(currentTab.elements[theElement.upNeighbour].thisElement);
			changeColour(theUpNeighbour,upColour,"up",hide);
		}
		if(theElement.downNeighbour != -1)
		{
			var theDownNeighbour = $(currentTab.elements[theElement.downNeighbour].thisElement);
			changeColour(theDownNeighbour,downColour,"down",hide);
		}
		if(theElement.leftNeighbour != -1)
		{
			var theLeftNeighbour = $(currentTab.elements[theElement.leftNeighbour].thisElement);
			changeColour(theLeftNeighbour,leftColour,"left",hide);
		}
		if(theElement.rightNeighbour != -1)
		{
			var theRightNeighbour = $(currentTab.elements[theElement.rightNeighbour].thisElement);
			changeColour(theRightNeighbour,rightColour,"right",hide);
		}
	}
}

function changeColour(element,colour,dir,hide)
{
	// Checkbox's can't change colour, so change their parent
	if(element.prop("type") == "checkbox")
	{
		if(!hide && !coloursCached) prevColours[dir] = element.parent().css("background-color");
		else prevColours[dir] = "";
		element.parent().css("background-color",colour).delay("500");
	}
	// Selectric boxes only change colour from the .selectric class
	else if(element.hasClass("selectricInput"))
	{
		if(!hide && !coloursCached) prevColours[dir] = element.siblings(".selectric").css("background-color");
		else prevColours[dir] = "";
		element.siblings(".selectric").css("background-color",colour).delay("500");
	}
	else
	{
		if(!hide && !coloursCached) prevColours[dir] = element.css("background-color");
		else prevColours[dir] = "";
		element.css("background-color",colour).delay("500");
	}
}
